// John Lake
// CSCE 441

#include <stdio.h>
#include <list>
#include "./ActiveEdgeTable.h"
#include <gl/glut.h>
#include <gl/gl.h>	//
#include <cmath>	// ?


/******************************************************************
	Notes:
	Image size is 400 by 400 by default.  You may adjust this if
		you want to.
	You can assume the window will NOT be resized.
	Call clearFramebuffer to clear the entire framebuffer.
	Call setFramebuffer to set a pixel.  This should be the only
		routine you use to set the color (other than clearing the
		entire framebuffer).  drawit() will cause the current
		framebuffer to be displayed.
	As is, your scan conversion should probably be called from
		within the display function.  There is a very short sample
		of code there now.
	You may add code to any of the subroutines here,  You probably
		want to leave the drawit, clearFramebuffer, and
		setFramebuffer commands alone, though.
  *****************************************************************/

#define ImageW 800
#define ImageH 800

float framebuffer[ImageH][ImageW][3];

std::list<Polygon*> polyList;	// keep track of (original) polygons drawn in window
std::list<Polygon*> clipList;	// keep track of polygons after clipping
Polygon* inProgress;			// keep track of the polygon currently being drawn
Point p1, p2;					// points used in the implementation
Point* origin;
Color next_color;				// used to change the color of the next polygon;
								//    changed on right click (after draw())
bool drawing_rectangle;



// simple function to change the color for the next polygon
void colorUpdate()
{
	double r1, r2;
	r1 = rand();
	r2 = rand();
	printf("new color: %f, %f\n", r1, r2);
	if(r1 > r2)
		next_color.r = r2/r1;	// number between 0 and 1
	else
		next_color.r = r1/r2;	// number between 0 and 1

	r1 = rand();
	r2 = rand();
	if(r1 > r2)
		next_color.g = r2/r1;	// number between 0 and 1
	else
		next_color.g = r1/r2;	// number between 0 and 1

	r1 = rand();
	r2 = rand();
	if(r1 > r2)
		next_color.b = r2/r1;	// number between 0 and 1
	else
		next_color.b = r1/r2;	// number between 0 and 1
}


/**
 *	Imports information about a polygon from a .obj file (in .obj format)
 *	  Assumes that the coordinates are in order (type, x, y, z) and that
 *	  nothing is specified to greater than 10 digits.
 */
void importPolygon(char* filename)
{
/*
	FILE* objFile;
	objFile = fopen(filename, "r");
*/
	printf("file is:\t%s\n",filename);

	vector<Pt> currPoly;
	char type[10], cx[10], cy[10], cz[10], space[256];
	float x,y,z;


	std::ifstream objFile(filename, ifstream::in);
	while(objFile.good())	// not EOF
	{
		objFile.getline(type, 10, ' ');	// use ' ' as delimiter
	
		if(type[0] == '#')
		{
			printf("Comment Line\n");
			objFile.getline(space,256);	// get the rest of the commented line
			continue;
		}
		if(type[0] == 'v')
		{
			objFile.getline(cx,10,' ');	// use ' ' as delimiter
			objFile.getline(cy,10,' ');	// use ' ' as delimiter
			objFile.getline(cz,10);		// will use '\n' as delimiter

			x = atof(cx);
			y = atof(cy);
			z = atof(cz);

			// For testing
			printf("%s | %f | %f | %f\n", type,x,y,z);

			// Handle adding vertex


		}
		if(type[0] == 'f')	// face
		{
			objFile.getline(cx,10,' ');	// use ' ' as delimiter
			objFile.getline(cy,10,' ');	// use ' ' as delimiter
			objFile.getline(cz,10);		// will use '\n' as delimiter


			x = atoi(cx);
			y = atoi(cy);
			z = atoi(cz);

			// For testing
			printf("%s | %f | %f | %f\n", type,x,y,z);

			// Handle face


		}



	}
}

// Draws the scene
void drawit(void)
{
   glDrawPixels(ImageW,ImageH,GL_RGB,GL_FLOAT,framebuffer);
   glFlush();
}

// Clears framebuffer to black
void clearFramebuffer()
{
	int i,j;

	for(i=0;i<ImageH;i++) {
		for (j=0;j<ImageW;j++) {
			framebuffer[i][j][0] = 0.0;	// R
			framebuffer[i][j][1] = 0.0;	// G
			framebuffer[i][j][2] = 0.0;	// B
		}
	}
}

// Sets pixel x,y to the color RGB
// I've made a small change to this function to make the pixels match
// those returned by the glutMouseFunc exactly - Scott Schaefer 
void setFramebuffer(int x, int y, float R, float G, float B)
{
	// changes the origin from the lower-left corner to the upper-left corner
	y = ImageH - 1 - y;
	if (R<=1.0)
		if (R>=0.0)
			framebuffer[y][x][0]=R;
		else
			framebuffer[y][x][0]=0.0;
	else
		framebuffer[y][x][0]=1.0;
	if (G<=1.0)
		if (G>=0.0)
			framebuffer[y][x][1]=G;
		else
			framebuffer[y][x][1]=0.0;
	else
		framebuffer[y][x][1]=1.0;
	if (B<=1.0)
		if (B>=0.0)
			framebuffer[y][x][2]=B;
		else
			framebuffer[y][x][2]=0.0;
	else
		framebuffer[y][x][2]=1.0;
}



void display(void)
{
	// if I had more time, I would implement the program so that drawPolygon
	// was called in here (but the program is already late)
	drawit();
}


void clipDisplay(void)
{	
	// if I had more time, I would implement the program so that drawPolygon
	// was called in here (but the program is already late)
	drawit();
}


void mouseDraw(int button, int state, int x, int y)
{
	Point* temp;	// probable exists better way to do this
	temp = new Point;
	temp->x = x;
	temp->y = y;

	if(state == GLUT_UP)	// catching the end of a down-press => ignore
		return;

	if(button == GLUT_LEFT_BUTTON)
	{
		if(inProgress == NULL)
		{
			origin = new Point;
			origin->x = temp->x;
			origin->y = temp->y;
			inProgress = new Polygon(ImageW, ImageH, next_color);
		}

		inProgress->vTable.push_back(temp);
	}

	else if(button == GLUT_RIGHT_BUTTON)
	{
		inProgress->vTable.push_back(temp);
		inProgress->vTable.push_back(origin);
		Edge* tempEdge;
		Point e1, e2;

		// eventually move this to drawPolygon();
		std::list<Point*>::iterator pIter = inProgress->vTable.begin();
		while(pIter != inProgress->vTable.end())
		{
			// copy two points
			e1.x = (*pIter)->x;
			e1.y = (*pIter)->y;

			pIter++;

			if(pIter == inProgress->vTable.end())
			{	// origin was already at the end; nothing to be done, break out
				break;
			}
			
			e2.x = (*pIter)->x;
			e2.y = (*pIter)->y;

			tempEdge = new Edge(e1, e2);
			inProgress->addEdge(tempEdge);
		}

		// put inProgress in the list of polygons
		polyList.push_back(inProgress);
		inProgress->drawPolygon();

		// trigger redrawing
		glutPostRedisplay();

		// clean up
		inProgress = NULL;
		
		// update next_color
		colorUpdate();
	}
	else
	{
		// something wrong?
		printf("\nWas neither left nor right button; please try a different input!\n\n");
	}

}

// the new function to be called after 
void mouseClip(int button, int state, int x, int y)
{
	clearFramebuffer();

	if(state == GLUT_DOWN)
	{
		p1.x = x; 
		p1.y = y;
		drawing_rectangle = true;


		// draw the old polygons
		std::list<Polygon*>::iterator polyListIter;
		for(polyListIter = polyList.begin(); polyListIter != polyList.end(); polyListIter++)
		{	// draw each polygon in the list
			(*polyListIter)->drawPolygon();	// calls setFramebuffer()
		}
		drawit();

	}

	if(state == GLUT_UP)
	{
		drawing_rectangle = false;

		// Sutherland-Hodgman()
		std::list<Polygon*>::iterator polyListIter;
		for(polyListIter = polyList.begin(); polyListIter != polyList.end(); polyListIter++)
		{	// draw each polygon in the list
			//clips the polygon and puts the result in the other list
			clipList.push_back((*polyListIter)->clipPolygon((*polyListIter),p1,p2));	
		}


		// draw the clipped polygons
		std::list<Polygon*>::iterator clipListIter;		
		for(clipListIter = clipList.begin(); clipListIter != clipList.end(); clipListIter++)
		{	// draw each polygon in the list
			(*clipListIter)->drawPolygon();	// calls setFramebuffer()
		}

		glutPostRedisplay();

		for(clipListIter = clipList.begin(); clipListIter != clipList.end(); 1)		
			clipListIter = clipList.erase(clipListIter);
	}
}

// motion function to draw the rectangle
void mouseClip_Rect(int x, int y)
{
//	if((x != p1.x) || (y != p2.y))	// if it's not the same point
//		clearFramebuffer();
	// draw the old polygons
	std::list<Polygon*>::iterator polyListIter;
	for(polyListIter = polyList.begin(); polyListIter != polyList.end(); polyListIter++)
	{	// draw each polygon in the list
		(*polyListIter)->drawPolygon();	// calls setFramebuffer()
	}

	// draw the clipping box
	p2.x = x;
	p2.y = y;
	glBegin ( GL_QUADS );
		glVertex2f ( p1.x, p1.y );
		glVertex2f ( p1.x, p2.y );
		glVertex2f ( p2.x, p2.y );
		glVertex2f ( p2.x, p1.y );
	glEnd ( );
	glFlush();

	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
	switch(key) {
	case 'c':
		// enter clipping mode

		//must now pull from the other list of polygons	
		glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);	// changed to GLUT_RGBA to allow blending for clipping rectangle
		glEnable (GL_BLEND);
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1, 1, 1, 0.1);	// use a transparent white rectangle

		glutDisplayFunc(clipDisplay);
		glutMouseFunc( mouseClip );
		glutMotionFunc( mouseClip_Rect );
		break;
		
	default:
		break;
	}
}

void init(void)
{
	gluOrtho2D ( 0, ImageW - 1, ImageH - 1, 0 );
	clearFramebuffer();
}

int main(int argc, char** argv)
{
	inProgress = NULL;
	next_color.r = 0.5;
	next_color.g = 0.5;
	next_color.b = 0.5;
	drawing_rectangle = false;

	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
	glutInitWindowSize(ImageW,ImageH);
	glutInitWindowPosition(100,100);
	glutCreateWindow("John Lake - Homework 2");
	init();	
	glutDisplayFunc(display);
	glutMouseFunc( mouseDraw );
	glutKeyboardFunc( keyboard );
	glutMainLoop();
	return 0;
}
